package com.meisupic.copyright.service.impl;

import static com.meisupic.copyright.common.Constants.AUTH_TYPE_ENTERPRISE;
import static com.meisupic.copyright.common.Constants.AUTH_TYPE_PERSON;
import static com.meisupic.copyright.common.Constants.REALNAME_AUTH_PROCESS_STATUS_PASSED;
import static com.yuanbenlian.service.ECKeyProcessor.GeneratorSecp256k1Key;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.meisupic.copyright.common.AuthProcessEnum;
import com.meisupic.copyright.common.Constants;
import com.meisupic.copyright.domain.bo.ApplyerSignBo;
import com.meisupic.copyright.domain.bo.UserRegisterBo;
import com.meisupic.copyright.domain.po.RealNameAuth;
import com.meisupic.copyright.domain.po.Users;
import com.meisupic.copyright.domain.query.RealNameAuthQuery;
import com.meisupic.copyright.domain.vo.PageRealNameAuthVo;
import com.meisupic.copyright.domain.vo.RealNameAuthVo;
import com.meisupic.copyright.exception.BizException;
import com.meisupic.copyright.repo.RealNameAuthRepo;
import com.meisupic.copyright.repo.UsersRepo;
import com.meisupic.copyright.service.IRealNameAuthService;
import com.meisupic.copyright.service.ITimevaleSignService;
import com.meisupic.copyright.support.HttpClient;
import com.meisupic.copyright.support.Response;
import com.meisupic.copyright.support.security.CustomPasswordEncode;
import com.meisupic.copyright.util.BeanMapper;
import com.meisupic.copyright.util.BeanUtil;
import com.meisupic.copyright.util.DateUtil;
import com.meisupic.copyright.util.StringUtil;
import com.yuanbenlian.model.SecretKey;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.commons.io.IOUtils;
import org.dozer.Mapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;

/**
 * creation wuxun
 *
 * @version V1.0
 * @Description: 实名认证service
 * @date 2018/1/13 22:59
 */
@Service
public class RealNameAuthServiceImpl implements IRealNameAuthService {

  @Autowired
  private RealNameAuthRepo realNameAuthRepo;

  @Autowired
  private ITimevaleSignService timevaleSignService;

  @Autowired
  private UsersRepo usersRepo;

  @Autowired
  private Mapper mapper;

  @Value("${dci.server.url}")
  private String dciServerUrl;

  @Value("${user.register.server.url}")
  private String userRegisterServerUrl;

  private Logger logger = LoggerFactory.getLogger(this.getClass());

  @Override
  public RealNameAuthVo findOne(RealNameAuthQuery query) {
    RealNameAuth realNameAuth = realNameAuthRepo.findOne(createSpecification(query));
    RealNameAuthVo realNameAuthVO = mapper.map(realNameAuth, RealNameAuthVo.class);
    return realNameAuthVO;
  }

  @Override
  public RealNameAuth findById(Long id) {
    return realNameAuthRepo.findOne(id);
  }

  @Override
  public RealNameAuth findByUserId(Long userId) {
    List<RealNameAuth> realNameAuthList = realNameAuthRepo.findByCreatorId(userId);
    if (StringUtil.isEmpty(realNameAuthList)) {
      return null;
    }
    return realNameAuthList.get(0);
  }

  @Override
  public List<RealNameAuthVo> findAll(RealNameAuthQuery query) {
    List<RealNameAuth> realNameAuthList = realNameAuthRepo.findAll(createSpecification(query));
    List<RealNameAuthVo> realNameAuthVOS = BeanMapper
        .mapList(realNameAuthList, RealNameAuthVo.class);
    return realNameAuthVOS;
  }

  @Override
  public RealNameAuth save(RealNameAuth realNameAuth) {

    //当ip地址识别错误时自动置空
    realNameAuth.setProvince(
        StringUtil.match("[\\u4e00-\\u9fa5]+", realNameAuth.getProvince()) ? realNameAuth
            .getProvince() : "");
    realNameAuth.setCity(
        StringUtil.match("[\\u4e00-\\u9fa5]+", realNameAuth.getCity()) ? realNameAuth.getCity()
            : "");

    if (StringUtil.isEmpty(realNameAuth.getId())) {
      return realNameAuthRepo.saveAndFlush(realNameAuth);
    } else {
      RealNameAuth realNameAuth_exist = realNameAuthRepo.findOne(realNameAuth.getId());
      if (REALNAME_AUTH_PROCESS_STATUS_PASSED.equals(realNameAuth_exist.getAuditStatus().name())) {
        throw new BizException("通过状态的实名认证不允许修改");
      }
      BeanUtil.copyPropertiesIgnoreNull(realNameAuth, realNameAuth_exist);
      return realNameAuthRepo.saveAndFlush(realNameAuth_exist);
    }
  }

  @Override
  public Page<PageRealNameAuthVo> findPage(RealNameAuthQuery realNameAuthQuery, Pageable pageable) {
    Specification<RealNameAuth> specification = createSpecification(realNameAuthQuery);
    Page<RealNameAuth> pageResult = realNameAuthRepo.findAll(specification, pageable);
    List<RealNameAuth> realNameAuthList = pageResult.getContent();
    List<PageRealNameAuthVo> realNameAuthVoList = BeanMapper
        .mapList(realNameAuthList, PageRealNameAuthVo.class);
    Page<PageRealNameAuthVo> realNameAuthVOPage = new PageImpl<>(
        realNameAuthVoList, pageable,
        pageResult.getTotalElements());
    return realNameAuthVOPage;
  }

  @Override
  public void personAuthAndSave(RealNameAuth realNameAuth) {

    if (realNameAuth.getId() == null) {
      isExist(realNameAuth.getCreatorId());
    }

    SecretKey secretKey = GeneratorSecp256k1Key();

    verifyPersonInfo(realNameAuth.getAuthName(), realNameAuth.getDocumetNo());
    String accountId = timevaleSignService.addPersonAccount(realNameAuth);
    realNameAuth.setAccountId(accountId);
    realNameAuth.setAuditStatus(AuthProcessEnum.passed);
    realNameAuth.setPrivateKey(secretKey.getPrivateKey());
    realNameAuth.setPublicKey(secretKey.getPublicKey());
    save(realNameAuth);

    pushApplyerToDci(realNameAuth);
  }


  @Override
  public void enterpriseAuthAndSave(RealNameAuth realNameAuth) {

    if (realNameAuth.getId() == null) {
      isExist(realNameAuth.getCreatorId());
    }

    SecretKey secretKey = GeneratorSecp256k1Key();

    verifyEnterpriseInfo(realNameAuth.getAuthName(), realNameAuth.getDocumetNo(),
        realNameAuth.getLegalName(), realNameAuth.getLegalIdentityCardNo());
    String accountId = timevaleSignService.addOrganizeAccount(realNameAuth);
    realNameAuth.setAccountId(accountId);
    realNameAuth.setAuditStatus(AuthProcessEnum.passed);
    realNameAuth.setPrivateKey(secretKey.getPrivateKey());
    realNameAuth.setPublicKey(secretKey.getPublicKey());
    save(realNameAuth);

    pushApplyerToDci(realNameAuth);
  }

  @Override
  public void updateRealNameAuth(RealNameAuth realNameAuth) {
    realNameAuthRepo.save(realNameAuth);
  }

  @Override
  public String pushApplyer(Long id) {
    RealNameAuth realNameAuth = realNameAuthRepo.findOne(id);

    ApplyerSignBo applyerSignBo = BeanMapper.map(realNameAuth, ApplyerSignBo.class);
    String appler = JSON.toJSONString(applyerSignBo);
    Map<String, String> params = new HashMap<>();
    params.put("esign_return", appler);
    String url = dciServerUrl + "/saveapplyerforesign.do";
    Response response = HttpClient.post(url, params);
    return JSON.toJSONString(response);
  }

  @Override
  public void saveApiRealNameAuth(RealNameAuth realNameAuth) {
    SecretKey secretKey = GeneratorSecp256k1Key();
    if (AUTH_TYPE_ENTERPRISE.equals(realNameAuth.getAuthType())) {
      verifyEnterpriseInfo(realNameAuth.getAuthName(), realNameAuth.getDocumetNo(),
          realNameAuth.getLegalName(), realNameAuth.getLegalIdentityCardNo());
      String accountId = timevaleSignService.addOrganizeAccount(realNameAuth);
      realNameAuth.setAccountId(accountId);
    } else if (AUTH_TYPE_PERSON.equals(realNameAuth.getAuthType())) {
      verifyPersonInfo(realNameAuth.getAuthName(), realNameAuth.getDocumetNo());
      String accountId = timevaleSignService.addPersonAccount(realNameAuth);
      realNameAuth.setAccountId(accountId);
    }
    realNameAuth.setAuditStatus(AuthProcessEnum.passed);
    realNameAuth.setPrivateKey(secretKey.getPrivateKey());
    realNameAuth.setPublicKey(secretKey.getPublicKey());

    if (StringUtil.isEmpty(realNameAuth.getId())) {
      //新创建一个虚拟用户
      Users users = createUser(realNameAuth.getMobile());
      realNameAuth.setCreatorId(users.getId());
      realNameAuth.setModifierId(users.getId());
      realNameAuthRepo.saveAndFlush(realNameAuth);
    } else {
      RealNameAuth realNameAuth_exist = realNameAuthRepo.findOne(realNameAuth.getId());
      if (REALNAME_AUTH_PROCESS_STATUS_PASSED.equals(realNameAuth_exist.getAuditStatus().name())) {
        throw new BizException("通过状态的实名认证不允许修改");
      }
      BeanUtil.copyPropertiesIgnoreNull(realNameAuth, realNameAuth_exist);
      realNameAuthRepo.saveAndFlush(realNameAuth_exist);
    }
    pushApplyerToDci(realNameAuth);
  }

  /**
   * rcode用户注册
   */
  @Override
  public void registerUser(RealNameAuth realNameAuth) {
    UserRegisterBo userRegisterBo = new UserRegisterBo();
    userRegisterBo.setCaid(realNameAuth.getAccountId());
    userRegisterBo.setRealName(realNameAuth.getAuthName());
    userRegisterBo.setIdcardNo(realNameAuth.getDocumetNo());
    userRegisterBo.setIdcardType(realNameAuth.getDocumentType().toString());
    userRegisterBo.setIdcardFile(getFileSuffix(realNameAuth.getDocumentImgPositive()));
    userRegisterBo.setIdentification1(getFileBase64FromUrl(realNameAuth.getDocumentImgPositive()));
    userRegisterBo.setAddress(realNameAuth.getProvince() + realNameAuth.getCity());
    userRegisterBo.setValidDateBegin(String.valueOf(DateUtil.addDays(new Date(), -5).getTime()));
    userRegisterBo.setProvince(realNameAuth.getProvince());
    userRegisterBo.setCity(realNameAuth.getCity());
    if (Constants.AUTH_TYPE_PERSON.equals(realNameAuth.getAuthType())) {
      userRegisterBo.setPeopleType("1");
    } else {
      userRegisterBo.setPeopleType("2");
    }
    userRegisterBo.setCompanyType("3");
    if (realNameAuth.getLongTermEffective() == 0) {
      userRegisterBo.setValidDateEnd(String.valueOf(realNameAuth.getInvalidDate().getTime()));
    }
    if (Constants.AUTH_TYPE_PERSON.equals(realNameAuth.getAuthType())) {
      userRegisterBo.setIdentification2(getFileBase64FromUrl(realNameAuth.getDocumentImgBack()));
    } else {
      userRegisterBo.setIdentification2(userRegisterBo.getIdentification1());
    }
    String remark =
        "1$" + userRegisterBo.getValidDateBegin() + "$" + userRegisterBo.getValidDateEnd();
    userRegisterBo.setRemark(remark);
    byte[] aesb = encryptAES(JSON.toJSONBytes(userRegisterBo), "78f1e83ed804fd7a");
    String base64encodedString = Base64.getEncoder().encodeToString(aesb);
    Response response = HttpClient.post(userRegisterServerUrl, base64encodedString, "UTF-8");
    logger.info("userRegister message"+JSON.toJSONString(response));
  }

  /**
   * 获取文件后缀
   */
  private String getFileSuffix(String fileUrl) {
    String ext = StringUtil.getExt(fileUrl);
    if("png".equals(ext)){
      return "PNG";
    }
    return "JPEG";
  }

  /**
   * 获取网络文件的base64
   */
  private String getFileBase64FromUrl(String url) {
    try {
      return Base64Utils.encodeToString(IOUtils.toByteArray(new URL(url)));
    } catch (IOException e) {
      logger.error("获取证件图片64失败", e);
      return null;
    }
  }

  private byte[] encryptAES(byte[] dataBytes, String password) {
    try {
      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
      SecretKeySpec key = new SecretKeySpec(password.getBytes("utf-8"), "AES");
      IvParameterSpec ivspec = new IvParameterSpec(password.getBytes("utf-8"));
      cipher.init(Cipher.ENCRYPT_MODE, key, ivspec);
      byte[] result = cipher.doFinal(dataBytes);

      return result;
    } catch (NoSuchPaddingException e) {
      e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    } catch (InvalidKeyException e) {
      e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
      e.printStackTrace();
    } catch (BadPaddingException e) {
      e.printStackTrace();
    } catch (InvalidAlgorithmParameterException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    return null;
  }

  /**
   * 同步信息到dci
   */
  public void pushApplyerToDci(RealNameAuth realNameAuth) {
    ApplyerSignBo applyerSignBo = BeanMapper.map(realNameAuth, ApplyerSignBo.class);
    String appler = JSON.toJSONString(applyerSignBo);
    Map<String, String> params = new HashMap<>();
    params.put("esign_return", appler);
    String url = dciServerUrl + "/saveapplyerforesign.do";
    Response response = HttpClient.post(url, params);
    logger.info(JSON.toJSONString(response));
  }


  private void isExist(Long userId) {
    RealNameAuth realNameAuth_exist = this.findByUserId(userId);
    if (!StringUtil.isEmpty(realNameAuth_exist)) {
      throw new BizException("已存在实名认证");
    }
  }

  /**
   * 调用E签宝接口进行个人实名认证
   */
  private void verifyPersonInfo(String name, String idCardNo) {

    JSONObject jsonObject = new JSONObject();
    jsonObject.put("name", name);
    jsonObject.put("idno", idCardNo);
    String personApply = jsonObject.toJSONString();
    String verifyResult = timevaleSignService.personAUth(personApply);
    JSONObject jsonResult = JSON.parseObject(verifyResult);
    int errorCode = jsonResult.getIntValue("errCode");
    if (errorCode != 0) {
      String errMsg = jsonResult.getString("msg");
      throw new BizException(errMsg);
    }
  }

  /**
   * 调用E签宝接口进行企业实名认证
   */
  private void verifyEnterpriseInfo(String name, String codeUSC, String legalName,
      String legalIdno) {
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("name", name);
    jsonObject.put("codeUSC", codeUSC);
    jsonObject.put("legalName", legalName);
    jsonObject.put("legalIdno", legalIdno);
    String enterpriseApply = jsonObject.toJSONString();
    String verifyResult = timevaleSignService.enterpriseAuth(enterpriseApply);
    JSONObject jsonResult = JSON.parseObject(verifyResult);
    int errorCode = jsonResult.getIntValue("errCode");
    if (errorCode != 0) {
      String errMsg = jsonResult.getString("msg");
      throw new BizException(errMsg);
    }
  }

  private Specification<RealNameAuth> createSpecification(RealNameAuthQuery query) {
    return new Specification<RealNameAuth>() {
      @Override
      public Predicate toPredicate(Root<RealNameAuth> root, CriteriaQuery<?> criteriaQuery,
          CriteriaBuilder criteriaBuilder) {
        List<Predicate> predicates = new ArrayList<>();
        if (!StringUtil.isEmpty(query.getUserId())) {
          predicates.add(criteriaBuilder.equal(root.get("userId"), query.getUserId()));
        }
        if (!StringUtil.isEmpty(query.getAuthName())) {
          predicates.add(criteriaBuilder.like(root.get("authName"), query.getAuthName()));
        }
        if (!StringUtil.isEmpty(query.getAuthType())) {
          predicates.add(criteriaBuilder.equal(root.get("authType"), query.getAuthType()));
        }
        if (!StringUtil.isEmpty(query.getCreateTimeBegin())) {
          predicates.add(criteriaBuilder
              .between(root.get("createTime"), query.getCreateTimeBegin(),
                  query.getCreateTimeEnd()));
        }
        return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
      }
    };
  }

  private Users createUser(String userName) {
    Users users = new Users();
    users.setUserName(userName);
    users.setPassword("123456");
    int salt = StringUtil.getRangeRandomCode(1000, 9999);
    users.setSalt(String.valueOf(salt));
    CustomPasswordEncode passwordEncode = new CustomPasswordEncode();
    String encodePwd = passwordEncode.encodePassword(users.getPassword(), users.getSalt());
    users.setPassword(encodePwd);
    usersRepo.save(users);
    return users;
  }
}
